package com.iwindplus.setup.server.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.iwindplus.boot.domain.enums.OssTypeEnum;
import com.iwindplus.boot.domain.vo.FilePathVO;
import com.iwindplus.boot.domain.vo.UploadVO;
import com.iwindplus.boot.oss.domain.property.OssProperty;
import com.iwindplus.boot.oss.service.OssAliyunService;
import com.iwindplus.boot.oss.service.OssMinioService;
import com.iwindplus.boot.oss.service.OssQiniuService;
import com.iwindplus.setup.domain.dto.OssUploadByteDTO;
import com.iwindplus.setup.domain.vo.OssConfigVO;
import com.iwindplus.setup.domain.vo.OssTplVO;
import com.iwindplus.setup.server.service.OssConfigService;
import com.iwindplus.setup.server.service.OssService;
import com.iwindplus.setup.server.service.OssTplService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

/**
 * 对象存储业务层接口实现类.
 *
 * @author zengdegui
 * @since 2021/7/20
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class OssServiceImpl implements OssService {

    @Resource
    private OssAliyunService ossAliyunService;

    @Resource
    private OssMinioService ossMinioService;

    @Resource
    private OssQiniuService ossQiniuService;

    @Lazy
    @Resource
    private OssConfigService ossConfigService;

    @Resource
    private OssTplService ossTplService;

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public UploadVO uploadByte(OssUploadByteDTO entity) {
        final OssTplVO ossTpl = this.ossTplService.getByCode(entity.getTplCode());
        final OssConfigVO ossConfig = this.ossConfigService.getDetail(ossTpl.getConfigId());
        if (OssTypeEnum.MINIO.equals(ossConfig.getType())) {
            this.buildOssConfigMinio(ossConfig, ossTpl);
            return this.ossMinioService.uploadFile(ArrayUtil.unWrap(entity.getData()), entity.getPrefix(), entity.getSourceFileName(),
                entity.getRenamed(), Boolean.TRUE);
        } else if (OssTypeEnum.ALIYUN.equals(ossConfig.getType())) {
            this.buildOssConfigAliyun(ossConfig, ossTpl);
            return this.ossAliyunService.uploadFile(ArrayUtil.unWrap(entity.getData()), entity.getPrefix(), entity.getSourceFileName(),
                entity.getRenamed(), Boolean.TRUE);
        } else if (OssTypeEnum.QINIU.equals(ossConfig.getType())) {
            this.buildOssConfigQiniu(ossConfig, ossTpl);
            return this.ossQiniuService.uploadFile(ArrayUtil.unWrap(entity.getData()), entity.getPrefix(), entity.getSourceFileName(),
                entity.getRenamed(), Boolean.TRUE);
        }
        return null;
    }

    @Override
    public UploadVO uploadFile(String tplCode, MultipartFile file, String prefix, Boolean renamed) {
        final OssTplVO ossTpl = this.ossTplService.getByCode(tplCode);
        final OssConfigVO ossConfig = this.ossConfigService.getDetail(ossTpl.getConfigId());
        if (OssTypeEnum.MINIO.equals(ossConfig.getType())) {
            this.buildOssConfigMinio(ossConfig, ossTpl);
            return this.ossMinioService.uploadFile(file, prefix, renamed, Boolean.TRUE);
        } else if (OssTypeEnum.ALIYUN.equals(ossConfig.getType())) {
            this.buildOssConfigAliyun(ossConfig, ossTpl);
            return this.ossAliyunService.uploadFile(file, prefix, renamed, Boolean.TRUE);
        } else if (OssTypeEnum.QINIU.equals(ossConfig.getType())) {
            this.buildOssConfigQiniu(ossConfig, ossTpl);
            return this.ossQiniuService.uploadFile(file, prefix, renamed, Boolean.TRUE);
        }
        return null;
    }

    @Override
    public void downloadFile(String tplCode, HttpServletResponse response, String relativePath, String fileName) {
        final OssTplVO ossTpl = this.ossTplService.getByCode(tplCode);
        final OssConfigVO ossConfig = this.ossConfigService.getDetail(ossTpl.getConfigId());
        if (OssTypeEnum.MINIO.equals(ossConfig.getType())) {
            this.buildOssConfigMinio(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossMinioService.downloadFile(response, relativePath, fileName));
        } else if (OssTypeEnum.ALIYUN.equals(ossConfig.getType())) {
            this.buildOssConfigAliyun(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossAliyunService.downloadFile(response, relativePath, fileName));
        } else if (OssTypeEnum.QINIU.equals(ossConfig.getType())) {
            this.buildOssConfigQiniu(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossQiniuService.downloadFile(response, relativePath, fileName));
        }
    }

    @Override
    public List<FilePathVO> listSignUrl(String tplCode, List<String> relativePaths, Integer timeout) {
        final OssTplVO ossTpl = this.ossTplService.getByCode(tplCode);
        final OssConfigVO ossConfig = this.ossConfigService.getDetail(ossTpl.getConfigId());
        if (OssTypeEnum.MINIO.equals(ossConfig.getType())) {
            this.buildOssConfigMinio(ossConfig, ossTpl);
            return this.ossMinioService.listSignUrl(relativePaths, timeout);
        } else if (OssTypeEnum.ALIYUN.equals(ossConfig.getType())) {
            this.buildOssConfigAliyun(ossConfig, ossTpl);
            return this.ossAliyunService.listSignUrl(relativePaths, timeout);
        } else if (OssTypeEnum.QINIU.equals(ossConfig.getType())) {
            this.buildOssConfigQiniu(ossConfig, ossTpl);
            return this.ossQiniuService.listSignUrl(relativePaths, timeout);
        }
        return null;
    }

    @Override
    public void removeFiles(String tplCode, List<String> relativePaths) {
        final OssTplVO ossTpl = this.ossTplService.getByCode(tplCode);
        final OssConfigVO ossConfig = this.ossConfigService.getDetail(ossTpl.getConfigId());
        if (OssTypeEnum.MINIO.equals(ossConfig.getType())) {
            this.buildOssConfigMinio(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossMinioService.removeFiles(relativePaths));
        } else if (OssTypeEnum.ALIYUN.equals(ossConfig.getType())) {
            this.buildOssConfigAliyun(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossAliyunService.removeFiles(relativePaths));
        } else if (OssTypeEnum.QINIU.equals(ossConfig.getType())) {
            this.buildOssConfigQiniu(ossConfig, ossTpl);
            this.threadPoolTaskExecutor.execute(() -> this.ossQiniuService.removeFiles(relativePaths));
        }
    }

    private void buildOssConfigMinio(OssConfigVO ossConfig, OssTplVO ossTpl) {
        OssProperty.MinioConfig minioConfig = OssProperty.MinioConfig.builder()
            .endpoint(ossConfig.getOssEndpoint())
            .accessKey(ossConfig.getAccessKey())
            .secretKey(ossConfig.getSecretKey())
            .bucketName(ossTpl.getBucketName())
            .accessDomain(ossTpl.getAccessDomain())
            .partSize(ossTpl.getPartSize())
            .build();
        OssProperty config = OssProperty.builder()
            .minio(minioConfig)
            .build();
        this.ossMinioService.setConfig(config);
    }

    private void buildOssConfigAliyun(OssConfigVO ossConfig, OssTplVO ossTpl) {
        final OssProperty.AliyunConfig.StsConfig stsConfig = OssProperty.AliyunConfig.StsConfig.builder()
            .endpoint(ossConfig.getStsEndpoint())
            .roleArn(ossConfig.getRoleArn())
            .policy(ossConfig.getPolicy())
            .build();
        final OssProperty.AliyunConfig aliyunConfig = OssProperty.AliyunConfig.builder()
            .endpoint(ossConfig.getOssEndpoint())
            .accessKey(ossConfig.getAccessKey())
            .secretKey(ossConfig.getSecretKey())
            .bucketName(ossTpl.getBucketName())
            .accessDomain(ossTpl.getAccessDomain())
            .partSize(ossTpl.getPartSize())
            .broke(ossTpl.getBroke())
            .sts(stsConfig)
            .build();
        OssProperty config = OssProperty.builder()
            .aliyun(aliyunConfig)
            .build();
        this.ossAliyunService.setConfig(config);
    }

    private void buildOssConfigQiniu(OssConfigVO ossConfig, OssTplVO ossTpl) {
        final OssProperty.QiniuConfig qiniuConfig = OssProperty.QiniuConfig.builder()
            .accessKey(ossConfig.getAccessKey())
            .secretKey(ossConfig.getSecretKey())
            .bucketName(ossTpl.getBucketName())
            .accessDomain(ossTpl.getAccessDomain())
            .partSize(ossTpl.getPartSize())
            .broke(ossTpl.getBroke())
            .build();
        OssProperty config = OssProperty.builder()
            .qiniu(qiniuConfig)
            .build();
        this.ossQiniuService.setConfig(config);
    }
}
